#pragma once
#include "Socket.hpp"
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <string>

using std::string;
using std::cout;
using std::cin;
using std::endl;

const int maxnum = (sizeof(fd_set) * 8);    //注意select最大可处理的文件描述符的数量是 “固定” 的
const int defaultfd = -1;
int debug_cnt = 0;

class SelectServer
{
public:
    SelectServer(){}
    SelectServer(uint16_t port)
    :_port(port)
    {}
    ~SelectServer(){}

    void InitServer()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        for(int i = 0; i < maxnum; i++)
        {
            _fd_array[i] = defaultfd;   //初始化_fd_array
        }
    }

    void Accpter()
    {
        //需要先遍历_fd_array找到一个“有效”位置，让accept获取到的fd “被关心”
        int newsock = _listensock.Accpet(nullptr, nullptr);
        if(newsock < 0)
        {
            return;
        }

        int pos = 1;
        for(; pos < maxnum; pos++)
        {
            if(_fd_array[pos] != defaultfd)
            {
                continue;
            }
            else
            {
                break;
            }
        }

        if(pos == maxnum)
        {
            cout << "shortage of fd_set" <<endl;
            close(newsock);
            return;
        }

        _fd_array[pos] = newsock;
    }

    void Receiver(int rfd, int pos)
    {
        char buffer[1024] = {0};
        ssize_t n = read(rfd, buffer, sizeof(buffer));      //注意读到的数据不一定完整噢
        if(n < 0)
        {
            cerr << "read error" << endl;
            close(rfd);
            _fd_array[pos] = defaultfd;     //表示pos位置可“使用”/“有效”
        }
        else if(n == 0)
        {   
            cout << "disconnect..." << endl;
            close(rfd);
            _fd_array[pos] = defaultfd;     //表示pos位置可“使用”/“有效”
        }
        else
        {
            cout << "echo: " << buffer << endl;
        }
    }

    void Dispatcher(fd_set* rfs)
    {
        cout << "this is " << debug_cnt++ << " time to dispatch" << endl;   //for debug
        //需要遍历_fd_array看哪些fd“有效”
        for(int i = 0; i < maxnum; i++)
        {
            if(_fd_array[i] == defaultfd)
            {
                continue;
            }      

            if(FD_ISSET(_fd_array[i], rfs))     //检查需要关心的fd是否已就绪     
            {
                //进行派发
                if(_fd_array[i] == _listensock.GetFd())
                {
                    Accpter();
                }
                else    //假设这里只有读事件
                {
                    Receiver(_fd_array[i], i);
                }
            }
        }
    }


    void DebugPrint(int n)
    {
        cout << "now we have fds: ";
        for(int i = 0; i < n; i++)
        {
            cout << _fd_array[i] << " ";
        }
        cout << endl;
    }

    void Start()
    {
        int listensock = _listensock.GetFd();
        _fd_array[0] = listensock;      //整个过程中，第一个就绪的事件一定是连接建立
        while(1)
        {
            //select
            fd_set rfs;
            int maxfd = 0;
            //timeval timeout = {5,0};
            FD_ZERO(&rfs);   //由于是在栈上开辟的，可能存在乱码，要先清零
            for(int i = 0; i < maxnum; i++)
            {
                if(_fd_array[i] == defaultfd)
                {
                    continue;
                }

                maxfd = max(maxfd, _fd_array[i]);
                FD_SET(_fd_array[i], &rfs);
            }

            //fix bug: 要先全都设置后才进行分派，否则就会有以下bug！
            //1.maxfd错误，导致卡在监听套接字处！
            //2.maxfd错误，导致select在底层遍历时，到不了对应的文件缓冲区，导致没有echo内容！
            //关键：分派过程不需要遍历，因为信息全在位图里了！

            DebugPrint(maxfd+1);

            int ret = select(maxfd+1,&rfs,nullptr,nullptr,nullptr);    
            if(ret < 0)
            {
                cerr << "select error" << endl;
            }
            else if(ret == 0)
            {
                cout << "not ready" << endl;
                sleep(1);
            }
            else    //链接建立后才可能分派
            {
                //TODO
                Dispatcher(&rfs);
            }                
            
        }
    }


private:
    int _fd_array[maxnum];      //存放需要关心的fd们, 注意读、写、异常需要分开存放，因为需要设置进不同的位图！
    Sock _listensock;
    uint16_t _port;
};